﻿namespace MathVisualizer

module Controls =

    open System
    open System.Windows.Forms
    open System.Drawing
    open System.Drawing.Drawing2D
    open System.Text.RegularExpressions
    open MathVisualizer.DrawLibrary 
    open MathVisualizer.Parser
    open MathVisualizer.Utils
    open MathVisualizer.Evaluator 
    open MathVisualizer.Render 
    open MathVisualizer.UserControls 
    

  

    type myTextBox() as x =
        inherit RichTextBox()

        let mutable highlighted = false
    
        let mutable firstpos = -1
        let mutable lastpos = -1
        let mutable ignore = false

      

        let deselect (currentpos:int) = 
            highlighted <- false
            debug("deselecting")
            x.SelectAll()
            //x.Select(firstpos, 1)
            x.SelectionColor <- Color.Black
            //x.Select(lastpos, 1)
            //x.SelectionColor <- Color.Black

            x.Select(currentpos + 1, 0)
            firstpos <- -1
            lastpos <- -1

        let selectPos current firstpos lastpos = 
            highlighted <- true
            //firstpos <- pos
            //lastpos <- i-1
            //debug("info: i: " + i.ToString())
            debug("firstpos: " + firstpos.ToString() + "; lastpos: " + lastpos.ToString())
            ignore <- true
            x.SelectionBackColor <- Color.White
            x.Select(firstpos, 1)
            x.SelectionColor <- Color.Red
            x.Select(lastpos, 1)
            x.SelectionColor <- Color.Red

            x.Select(current, 0)
            ignore <- false
            
        member x.insertText str =
            let oldsel = x.SelectionStart 
            x.Text <- x.Text.Insert(x.SelectionStart, str)
            x.SelectionStart <- oldsel

        member x.moveCursor offset =
            if (offset< x.Text.Length - x.SelectionStart) then
                x.SelectionStart <- x.SelectionStart + offset

        override x.OnSelectionChanged arg =
            //base.OnSelectionChanged(arg)
        
            let pos = x.SelectionStart - 1 
            let mutable str = x.Text
            if highlighted && not ignore then deselect(pos)

            if (pos>=0 && not ignore) then
                match x.Text.Chars(pos) with
                    |'('->  
                    
                        let mutable opened = 1
                        let mutable i = pos + 1

                        while (i < str.Length && opened>0) do
                            match str.Chars(i) with
                                | '(' -> opened <- opened + 1
                                | ')' -> opened <- opened - 1
                                | _ -> ()

                            i <- i+1
                        if (opened = 0 && i>0) then
                            selectPos (pos+1) (pos) (i-1)
                    | ')' ->
                        let mutable closed = 1
                        let mutable i = pos - 1

                        while (i >= 0 && closed>0) do
                            match str.Chars(i) with
                                | '(' -> closed <- closed - 1
                                | ')' -> closed <- closed + 1
                                | _ -> ()
                            i <- i-1
                        if (closed = 0) then
                            selectPos (pos+1) (i+1) (pos)
                    | _ -> ()

                x.Invalidate()
            ()



    type mathPanel() as x =
        inherit Panel()
    
        let mutable minfo = new mouseInfo(false, Over, new PointF(0.f, 0.f))
        let mutable treeValid = false
        let mutable lastOver = Expr(Empty)
        let mutable lastClicked = Expr(Empty)
        let mutable title = ""
        let mutable bitmap = new Bitmap(1, 1)
        let titlefont = new Font(titlefontfamily, 20.f)
        let txtinput = new myTextBox()
        
        let mutable e = Expr(Empty)


        let updateevent = new Event<_>()
        let mutable currentmessage = "Ok"

        let setText str = 
            txtinput.Text <- str
            txtinput.SelectionStart <- txtinput.Text.Length

        do
            x.Controls.Add(txtinput)
            txtinput.Dock <- DockStyle.Bottom
            txtinput.Font <- new Font(new FontFamily("Courier new"), 12.f)
            lastClicked <- e
            txtinput.TextChanged.Add(fun arg ->
                                        match lastClicked.Expr with
                                            |_ -> 
                                                try
                                                    debug("text ok")
                                                    let mutable newe = Expr(Empty) 
                                                    currentmessage<-"Ok"
                                                    try
                                                        newe <- parse2(txtinput.Text)
                                                    with
                                                        | InvalidExpression(str) -> currentmessage<-str
                                                        | _ -> currentmessage<-"Invalid Expression"
                                                    
                                                    updateevent.Trigger(x, currentmessage)
                                                            
                                                    if lastClicked = e then 
                                                                        e<-newe
                                                                        e.Clicked <- true
                                                                        lastClicked <- e
                                                    else lastClicked.Expr <- newe.Expr
                                                         lastClicked.ExprString <- newe.ExprString 
                                                    treeValid <- false
                                                    x.Invalidate()
                                                with
                                                    | err -> debug("Errore: " + err.ToString()) 
                                        ())

        member x.moveCursor offset =
            txtinput.moveCursor offset 

        member x.insertText txt =
            txtinput.insertText txt

        member x.setTitle newtitle =
            title <- newtitle

        member x.getCurrentImage =
            bitmap

        member x.getCurrentExprString =
            e.ExprString 

        [<CLIEvent>]
        member x.ExprUpdated = updateevent.Publish

        override x.OnPaint arg = 
            if (not treeValid) then 
                eval1(arg.Graphics, e, 0)
                treeValid <- true   

            bitmap.Dispose()        
            bitmap <- new Bitmap(x.Width, x.Height - txtinput.Height)       
            let g1 = Graphics.FromImage(bitmap)       
            g1.FillRectangle(Brushes.White, 0, 0, bitmap.Width, bitmap.Height)
            g1.DrawString(title, titlefont, Brushes.Gray, PointF(0.f, 0.f))
            
            let off = new PointF(float32 bitmap.Width / 2.f - e.Container.Width / 2.f, float32 bitmap.Height / 2.f - e.Container.Height / 2.f)    
            paintTree(g1, off, e, 0, minfo)  
            arg.Graphics.DrawImage(bitmap, 0, 0)      
            //System.GC.Collect()
            ()

        
        override x.OnMouseMove arg =
            minfo.handle <- true
            minfo.event <- Over 
            minfo.position <- toPointF(arg.Location)
            lastOver.MouseOver <- false
            lastOver <- mouseHandler minfo e
            lastOver.MouseOver <- true
            printf "LASTOVER: %s; e.exprstr: %s\n" lastOver.ExprString e.ExprString 
            match lastClicked.Expr with
                |Empty ->
                    setText lastOver.ExprString 
                | _ -> ()

            x.Invalidate()
            ()
        
        
        override x.OnMouseDown arg = 
            minfo.handle <- true
            minfo.event <- Click
            minfo.position <- toPointF(arg.Location) 
            lastClicked.Clicked <- false
            let tmp = mouseHandler minfo e
            lastClicked <- match tmp.Expr, e.Expr with
                            | Empty, Empty -> e
                            | _, _ -> tmp
            lastClicked.Clicked <- true
            setText lastClicked.ExprString
            printf "MouseClick: lastclicked is Empty? %b; e is empty: %b\n" (lastClicked.Expr = Empty) (e.Expr = Empty)
            x.Invalidate()
            ()
        
        override x.OnPaintBackground arg = ()
        
        
        
        override x.OnResize arg = 
            base.OnResize(arg)
            x.Invalidate()
    

